#ifndef __MODELO_H__
#define __MODELO_H__

#include <map>

#define SimboloESC 256
#define SimboloEOF 257

typedef short Simbolo;

class Modelo;

class DatoSimboloModelo
{
    public:
    
    unsigned long frecuencia;
    Modelo *modeloSiguienteOrden;
    unsigned long ultimaPosicionEnQueSeEncontro;
};

class DatosSimbolosModelo:public std::map<Simbolo,DatoSimboloModelo>
{
};

class FrecuenciasSimbolos:public DatosSimbolosModelo
{
    public:
    
    /**
    * Agrega todos los simbolos en frecuenciasSimbolos a this.
    * @param frecuenciasSimbolos Símbolos a incluir en this.
    */
    void addSimbolos(const FrecuenciasSimbolos &frecuenciasSimbolos,bool agregarESC=false);
};

class Modelo
{

    protected:
    
    DatosSimbolosModelo datosSimbolos;
    
    /**
    * @pre: El símbolo no debe existir en el modelo.
    * Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden 
    * NULL.
    */
    virtual void addSimbolo(const Simbolo simbolo);
    
    /**
     * @return Una instancia de la clase que implementa este método.
     */
    virtual Modelo *crearInstancia();

    public:
    
    /**
    * @param agregarEsc Indica si se debe agregar Simbolo::Esc a this al 
    * construir el modelo.
    */
    Modelo(bool agregarEsc=true);
    
    virtual ~Modelo();

    /**
    * @return true si el simbolo se encuentra dentro del modelo, sino false.
    */
    bool contiene(const Simbolo simbolo);
    
    /**
    * Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
    * de simbolos del modelo (también actualiza el ESC en caso de agregar).
    * @param simbolo Simbolo a ser agregado o quitado.
    */
    virtual void actualizarFrecuencia(const Simbolo simbolo);
    
    /**
    * Quita el simbolo del modelo.
    * @param simbolo Simbolo a ser quitado.
    */
    void removeSimbolo(const Simbolo simbolo);
    
    /**
    * @return Las frecuencias de los simbolos que se encuentran en el modelo.
    */
    const FrecuenciasSimbolos &getFrecuenciasSimbolos();
    
    /**
    * Obtiene para el símbolo pasado el modelo de siguiente orden.
    * @para simbolo Simbolo a buscar para el cual se obtendrá el modelo de 
    * siguiente orden.
    * @return El modelo de siguiente orden. NULL en caso que no exista modelo 
    * para el símbolo pasado.
    */
    Modelo *getModeloSiguienteOrden(const Simbolo simbolo);
    
    /**
    * @pre Debe existir el simbolo en el modelo.
    * Se crea un nuevo modelo de un orden mayor para el simbolo a insertar al 
    * final del contexto en caso que el mismo no exista, sino no se hace nada.
    * @param simbolo Simbolo para el cual se creará el modelo en caso que no 
    * exista.
    */
    Modelo *crearModeloSiguienteOrdenSiNoExiste(const Simbolo simbolo);
    
    /**
     * @pre El simbolo debe existir.
     * @param simbolo Simbolo para el cual obtendra la frecuencia.
     * @return La frecuencia del simbolo.
     */
    unsigned long getFrecuenciaSimbolo(const Simbolo simbolo);

};

class ModeloMenosUno:public Modelo
{
	public:

	ModeloMenosUno();

	virtual ~ModeloMenosUno();
};

class ModeloConEnvejecimientoHiperbolico:public Modelo
{
	protected:

    /**
    * @pre: El símbolo no debe existir en el modelo.
    * Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden
    * NULL.
    */
    virtual void addSimbolo(const Simbolo simbolo);

    /**
     * @return Una instancia de la clase que implementa este método.
     */
    virtual Modelo *crearInstancia();

	public:

	ModeloConEnvejecimientoHiperbolico();

	virtual ~ModeloConEnvejecimientoHiperbolico();

    /**
    * Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
    * de simbolos del modelo (también actualiza el ESC en caso de agregar).
    * @param simbolo Simbolo a ser agregado o quitado.
    */
    virtual void actualizarFrecuencia(const Simbolo simbolo);

};

class ModeloConEnvejecimientoExponencial:public Modelo
{
	protected:

	unsigned long terminoIndependiente;
	unsigned long frecuenciaTotal;
	double K;

    /**
    * @pre: El símbolo no debe existir en el modelo.
    * Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden
    * NULL.
    */
    virtual void addSimbolo(const Simbolo simbolo);

    /**
     * @return Una instancia de la clase que implementa este método.
     */
    virtual Modelo *crearInstancia();

    unsigned long getIncremento();

	public:

	ModeloConEnvejecimientoExponencial(
		const unsigned long terminoIndependiente=2,const double K=0.1425);

	virtual ~ModeloConEnvejecimientoExponencial();

    /**
    * Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
    * de simbolos del modelo (también actualiza el ESC en caso de agregar).
    * @param simbolo Simbolo a ser agregado o quitado.
    */
    virtual void actualizarFrecuencia(const Simbolo simbolo);

};

class ModeloCuadratico:public Modelo
{
	protected:

	/**
    * @pre: El símbolo no debe existir en el modelo.
    * Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden
    * NULL.
    */
    virtual void addSimbolo(const Simbolo simbolo);

    /**
     * @return Una instancia de la clase que implementa este método.
     */
    virtual Modelo *crearInstancia();

	public:

    ModeloCuadratico();

	virtual ~ModeloCuadratico();

    /**
    * Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
    * de simbolos del modelo (también actualiza el ESC en caso de agregar).
    * @param simbolo Simbolo a ser agregado o quitado.
    */
    virtual void actualizarFrecuencia(const Simbolo simbolo);

};

class ModeloInicializaEsc:public Modelo
{
	protected:

    /**
    * @pre: El símbolo no debe existir en el modelo.
    * Agrega el simbolo al modelo con frecuencia 1 y modelo de siguiente orden
    * NULL.
    */
    virtual void addSimbolo(const Simbolo simbolo);

    /**
     * @return Una instancia de la clase que implementa este método.
     */
    virtual Modelo *crearInstancia();

	public:

    ModeloInicializaEsc();

	virtual ~ModeloInicializaEsc();

    /**
    * Se actualiza la frecuencia del símbolo o se agrega el simbolo en las frecuencias
    * de simbolos del modelo (también actualiza el ESC en caso de agregar).
    * @param simbolo Simbolo a ser agregado o quitado.
    */
    virtual void actualizarFrecuencia(const Simbolo simbolo);

};
#endif

